#pragma once

#include <iostream>
#include <string>
#include <vector.h>
#include "Envelope.h"

using namespace std;
#include <FL/Fl_Widget.H>


#define EVT_NOTE_ON        1
#define EVT_NOTE_OFF       0

/*
typedef struct
{
    double time;
    double value;
}
Point;
*/






typedef struct
{
    double gain;
    double speed;
}
eventData;

typedef struct
{
    unsigned long sampleOffset;
    int eventType;
    eventData * data;
}
pluginEvent;


class GenericPlugin
{
    private:
        GenericPlugin * pMaster;


    public:
        unsigned long totalSamples;
        unsigned long samplesSinceNoteStart;
        bool bIsPolyphonic;
        bool bIsSlave;
        bool bIsFree;
        int noteId;

        vector<pluginEvent*> currentEvents; //this gets cleared after a process replacing

        vector<GenericPlugin*> slaves;

        //Envelope * pGenericNoteEnvelopes;
        //vector<Envelope*> songEnvelopes;
        //vector<EnvelopeEquation> envelopeEquations;
       // vector<Envelope *> individualNotesEnvelopes;


        int slaveId;
        /* if this is the master then slaveId gets incremented on each newSlave,
                            but if it is the slave it contain a unique id for the slave,
                            newly created slaves get their slaveId set to the masters slaveId++*/

        double * params;
        int numOfParams;

        GenericPlugin();
        GenericPlugin* getAFreeSlave(int noteId);
        void removeInactiveSlaves();
        void preProcessBuffer( float *outputBuffer, unsigned long framesPerBuffer, unsigned long totalSamplesIn);
        void addEvent(pluginEvent*);
        void preInitMaster();
        double getValue(unsigned int param);
        virtual void initMaster() {};

        virtual Fl_Widget * displayWindow() {};
        virtual void isFree() {};
        virtual void newSlave() {};
        virtual void handleEvent( pluginEvent* event ) {};
        virtual void processBuffer( float *outputBuffer, unsigned long framesPerBuffer ) {};
};


///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////

template<class T> GenericPlugin* genericPlugin_factory()
{
    return new T;
}

typedef GenericPlugin* (*genericPlugin_creator)(void);

class GenericPluginRegistry
{
    private:
        std::vector<genericPlugin_creator> m_genericPlugins;
    public:
        typedef std::vector<genericPlugin_creator>::iterator iterator;
        static GenericPluginRegistry& get();
        void add(genericPlugin_creator);
        iterator begin();
        iterator end();
};

class GenericPluginRegistration
{
    public:
        GenericPluginRegistration(genericPlugin_creator);
};

#define AUTO_REGISTER_GENERIC_PLUGIN(genericPlugin)    GenericPluginRegistration _genericPlugin_registration_ ## genericPlugin(&genericPlugin_factory<genericPlugin>);
